/**
 * 
 */
package truerefactor.input;

import japa.parser.JavaParser;
import japa.parser.ParseException;
import japa.parser.ast.CompilationUnit;
import japa.parser.ast.body.TypeDeclaration;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import truerefactor.graph.CodeGraph;

/**
 * CodeGraphBuilder - The director in the Builder Patter comprising the Input
 * Subsystem. This class directs the operations of the Graph Parser currently in
 * use and generates from code a complete CodeGraph representation and returns
 * this to the SystemController.
 * 
 * @author Isaac Griffith
 */
public class CodeGraphBuilder {

    /**
     * total language dependent Lines of Actual Code of a given Code Base.
     */
    private int totalLOC = 0;
    /**
     * The Code to Graph Parser to be used
     */
    private GraphParser parser;
    /**
     * The Code Graph to be created
     */
    private CodeGraph graph;

    /**
     * Constructs a new CodeGraphBuilder instance designated to use the provided
     * GraphParaser
     * 
     * @param parser
     */
    public CodeGraphBuilder(GraphParser parser)
    {
        this.parser = parser;
    }

    /**
     * Begins the process of converting source code into a code graph. The file
     * source is the root directory where the source files are stored.
     * 
     * @param graph
     *            Code Graph to be completed by this class
     * @param source
     *            root directory of the code base
     */
    public void process(CodeGraph graph, File source)
    {
        this.graph = graph;
        parser.setGraph(graph);

        List<File> files = this.generateSourceFileList(source);

        for (File file : files)
        {
            parseAndBuildFromFile(graph, file);
        }

        graph.connectClasses();
        graph.connectPackages();
        graph.connectMethods();
        graph.connectAttributes();
    }

    /**
     * Parses and develops a provided CodeGraph from the provided Source file.
     * 
     * @param graph
     *            the graph into which elements from the source file can be
     *            inserted.
     * @param source
     *            a single source code file from the code base
     */
    public void parseAndBuildFromFile(CodeGraph graph, File source)
    {
        try
        {
            totalLOC += parser.getLinesOfCode(source);
            CompilationUnit cu = JavaParser.parse(source);

            List<TypeDeclaration> types = cu.getTypes();
            for (TypeDeclaration type : types)
            {
                parser.setFile(source);
                parser.start(type);
            }

            parser.buildConnections(cu);
        }
        catch (ParseException ex)
        {
            System.out.println(source);
            ex.printStackTrace();
        }
    }

    /**
     * @return the totalLOC
     */
    public int getTotalLOC()
    {
        return totalLOC;
    }

    /**
     * Recusive method to list the contents of all subdirectories of a root
     * directory.
     * 
     * @param file
     *            the root directory containing files to be listed.
     * @return list of all files matching the known file type to be parsed
     */
    private List<File> recursiveDirListing(File file)
    {
        List<File> files = new ArrayList<File>();
        if (file.isDirectory())
        {
            for (File f : file.listFiles())
            {
                if (f.getName().startsWith("."))
                    continue;
                if (f.isDirectory())
                {
                    files.addAll(recursiveDirListing(f));
                }
                else if (f.getName().endsWith(JavaGraphParser.EXT))
                {
                    files.add(f);
                }
            }
        }

        return files;
    }

    /**
     * starts the recursive listing of all known files from a base path, that
     * match the file extension pattern for the parser.
     * 
     * @param basePath
     *            root path of the code base
     * @return all known source code files in the directory tree of the base
     *         path
     */
    private List<File> generateSourceFileList(File basePath)
    {
        List<File> files = new ArrayList<File>();

        if (basePath.isDirectory())
            files.addAll(recursiveDirListing(basePath));

        return files;
    }
}
